home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Group Library 1996 July
/
C-C++ Users Group Library July 1996.iso
/
vol_100
/
134_01
/
corodoc.nro
< prev
next >
Wrap
Text File
|
1985-08-19
|
24KB
|
617 lines
.pl 66
.rm 65
.m1 4
.m2 2
.m3 2
.m4 4
.he 'BDS C Coroutine Package''Introduction'
.fo '28 May 1983'-#-'K. B. Kenny'
.ls 1
.ce 3
Coroutine Package for BDS C
by
Kevin B. Kenny
.sp 2
.ul 1
Acknowledgements.
.sp
.ti +5
The BDS 'C' coroutine package is based on one developed at the University of
Waterloo for the programming language 'B' by I. D. Allen. The author
is also indebted to Peter Fraser, who explained the Waterloo implementation
and provided much good advice for the 'C' version.
.sp 2
.ul 1
1. Introduction: What are coroutines?
.sp
.ti +5
Every programmer is familiar with the concept of subroutines; portions
of a program that, in effect, provide extensions to the hardware and
allow the programmer to think of complex tasks as sequences of simpler
ones. While this way of thinking about subroutines is enormously useful,
another view is possible: a main program and its subroutines can be seen
as a team of actors, each with its own job to do. When the main program
has a task that some subroutine performs, it asks that subroutine for service;
when the subroutine finishes its job, it exits to the main program.
.sp
.ti +5
When a very complex subroutine is written, it is difficult to see it as
simply performing a service for the main program; it might be equally valid
to say that when the subroutine exits to the main program, it is asking
the main program to perform a service for it. The main program does its
duty, then returns to the subroutine for further orders.
.sp
.ti +5
Consider, for instance, a program which has very complicated operations
to perform on an input stream, eventually producing an output stream.
If the input routine is very complicated, we are tempted to make it the
"main program" and generate the output stream by calling an "output
subroutine". If, on the other hand, the output procedure is complex,
we are tempted to make
.ul 1
it
the "main program," retrieving its data from the "input subroutine."
What do we do when they are both complicated procedures and neither one
can conveniently be made into a subroutine?
.sp
.ti +5
The solution to this problem is to use
.ul 1
coroutines.
Coroutines are components of a program which are like subroutines in that
they are called from outside (although the correct technical term is
that they are
.ul 1
resumed).
Unlike subroutines, however, neither one of them is the "main program."
Each can call on the other in as many places as necessary; when one needs
a service (from its point of view) it resumes the other one. Rather than
entering it from the beginning, however, the machine picks up the
resumed coroutine
.ul 1
exactly where it left off.
The second coroutine then continues processing until, from
.ul 1
its
point of view, it needs the services of the first. It then resumes the
first coroutine, which is entered where it left off processing.
.sp
.ti +5
To each of the coroutines, then, the other appears as a subroutine. The
first resumes the second, and the second returns to the first. This
is the fundamental difference between subroutines and coroutines: while
a subroutine is always entered at the beginning, a coroutine is always
entered at the point where it left off.
.he 'BDS C Coroutine Package''Rationale'
.sp 3
.ul 1
2. Rationale: Why use coroutines?
.sp
.ti +5
It is difficult to come up with a simple example of the use of coroutines,
since they are most useful in interfacing two or more complicated tasks.
Generally, if one of the tasks is fairly simple, it is easier to implement
as a subroutine; making a coroutine of it seems contrived.
.sp
.ti +5
The most common uses of coroutines in practice arise either in terms of
multi-pass algorithms or in simulation. Multi-pass algorithms can use
coroutines to implement the passes, with each resuming the next when it
has output to be processed by the next pass and resuming the prior pass when
input is required.
Simulations can use coroutines to act like independent processes; for instance,
a simulation of a machine shop might use one coroutine to represent each
machine and pass the simulated workpiece around by having these coroutines
resume one another.
.sp
.ti +5
The first of these, the multipass algorithm, is probably more familiar to
C programmers, since Unix pipelines operate this way. When the user enters
a command like
.sp
.ti +10
.bo
foo | bar | zot
.sp
the system sets up three coroutines, corresponding to the commands "foo,"
"bar," and "zot."
It then enters the "zot" coroutine to begin the processing.
.sp
.ti +5
Things proceed normally until the first time "zot" needs a character from the
standard input stream. When it does, it calls "getchar" as usual; "getchar"
sees that "zot" has a predecessor in the pipeline. Rather than reading the
character from the terminal or some file, it resumes "bar" to get the
character.
.sp
.ti +5
Now "bar" starts running, and eventually needs an input character itself. When
it does, it calls "getchar," who sees that "bar" isn't the first command in the
pipeline either. As before, it resumes the preceding coroutine, "foo."
"Foo" runs normally, and since it is the first coroutine in the pipe, its calls
to "getchar" cause input from the standard input stream.
.sp
.ti +5
Eventually, "foo" has some output to place on the output stream. It calls
"putchar," who sees that "foo" isn't the last command in the pipeline. Instead
of writing the character somewhere, "putchar" resumes the "bar" coroutine,
passing the character to it. Since "bar" left off processing in "getchar"
where it resumed "foo", it is in just the right state to pick up the character
and continue processing, exactly as if it had been the first command in
the pipeline and got the character from the input stream.
.sp
.ti +5
"Foo" and "bar" continue passing and receiving characters until "bar" has
output to the output stream. As with "foo", it calls "putchar," who sees
that "bar" isn't the last command in the pipe, either. It then resumes
"zot", who has been waiting all this time for its first input character.
"Zot" is, of course, still in "getchar;" he picks up the character which
"bar" gave to "putchar" and continues processing. Since "zot" is the
last coroutine in the pipeline, its calls to "putchar" go on the standard
output stream.
.sp
.ti +5
The disc for the coroutine package contains a sample program organized as
a Unix-style pipeline, in the file "retab.c". "Retab" consists of the two
programs "entab" and "detab" from
.ul 1
Software Tools,
connected together in the sequence
.sp
.ti +10
.bo
detab | entab
.sp
so that the combination will convert the tabs in its input file to spaces,
and then reconvert them to tabs, possibly at a different set of tab stops.
.sp 3
.he 'BDS C Coroutine Package''Functions Available'
.ul 1
3. Functions Available in the Coroutine Package.
.sp
.ti +5
The most basic functions available in the coroutine package are C_CREATE,
which creates a coroutine; C_RESUME, which passes control to another coroutine,
and C_DESTROY, which destroys a coroutine. All of these primitives operate
on a dynamically-allocated area of memory called the
.ul 1
"function control vector (FCV)."
C_CREATE obtains an area of memory from free storage and initializes it as
an FCV, C_RESUME accepts an FCV and transfers into the associated coroutine,
and C_DESTROY accepts an FCV and recovers the space used.
.sp
.ti +5
While these functions are sufficient to implement any application involving
coroutines, the BDS C package supplies several additional primitives to make
certain jobs somewhat easier.
.sp
.ti +5
First, each coroutine has one word of user-specified information in its FCV.
This word, which is set by C_CREATE, can be examined by C_GETINFO and
modified by C_SETINFO. Normally this word will be a pointer to a structure
containing static storage for the coroutine to use. For instance, if
the coroutine is modelling a server in a queueing network, the area might
contain head and tail pointers for the queue of transactions waiting